"""Simple Javascript evaluator using ctypes and spidermonkey.

Some code from python-spidermonkey.
"""

from ctypes import *

STACK_CHUNK_SIZE = 8192

class JSString(Structure):
    _fields_ = []

class JSContext(Structure):
    _fields_ = []

class JSObject(Structure):
    _fields_ = []

class JSClass(Structure):
    _fields_ = []

class JSObjectOps(Structure):
    _fields_ = []

class JSXDRState(Structure):
    _fields_ = []

JSPropertyOp = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject), c_long, POINTER(c_long))
JSEnumerateOp = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject))
JSResolveOp = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject), c_long)
JSConvertOp = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject), c_int, POINTER(c_long))
JSFinalizeOp = CFUNCTYPE(None, POINTER(JSContext), POINTER(JSObject))
JSGetObjectOps = CFUNCTYPE(POINTER(JSObjectOps), POINTER(JSContext), POINTER(JSClass))
JSCheckAccessOp = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject), c_long, c_int, POINTER(c_long))
JSNative = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject), c_uint, POINTER(c_long), POINTER(c_long))
JSXDRObjectOp = CFUNCTYPE(c_int, POINTER(JSXDRState), POINTER(POINTER(JSObject)))
JSHasInstanceOp = CFUNCTYPE(c_int, POINTER(JSContext), POINTER(JSObject), c_long, POINTER(c_int))
JSMarkOp = CFUNCTYPE(c_uint, POINTER(JSContext), POINTER(JSObject), c_void_p)


class JSClass(Structure):
    _fields_ = [
        ('name', c_char_p),
        ('flags', c_uint),
        ('addProperty', JSPropertyOp),
        ('delProperty', JSPropertyOp),
        ('getProperty', JSPropertyOp),
        ('setProperty', JSPropertyOp),
        ('enumerate', JSEnumerateOp),
        ('resolve', JSResolveOp),
        ('convert', JSConvertOp),
        ('finalize', JSFinalizeOp),
        ('getObjectOps', JSGetObjectOps),
        ('checkAccess', JSCheckAccessOp),
        ('call', JSNative),
        ('construct', JSNative),
        ('xdrObject', JSXDRObjectOp),
        ('hasInstance', JSHasInstanceOp),
        ('mark', JSMarkOp),
        ('spare', c_long),
    ]

libsmjs = CDLL('libsmjs.so.1.5')

def evaluate(script):
    rt = libsmjs.JS_Init(1000000)
    cx = libsmjs.JS_NewContext(rt, STACK_CHUNK_SIZE)

    global_class = JSClass()
    global_class.name = "global"
    global_class.flags = 0
    global_class.addProperty = JSPropertyOp(libsmjs.JS_PropertyStub)
    global_class.delProperty = JSPropertyOp(libsmjs.JS_PropertyStub)
    global_class.getProperty = JSPropertyOp(libsmjs.JS_PropertyStub)
    global_class.setProperty = JSPropertyOp(libsmjs.JS_PropertyStub)
    global_class.enumerate = JSEnumerateOp(libsmjs.JS_EnumerateStub)
    global_class.resolve = JSResolveOp(libsmjs.JS_ResolveStub)
    global_class.convert = JSConvertOp(libsmjs.JS_ConvertStub)
    global_class.finalize = JSFinalizeOp(libsmjs.JS_FinalizeStub)

    global_object = libsmjs.JS_NewObject(cx, byref(global_class), 0, 0)
    libsmjs.JS_InitStandardClasses(cx, global_object)

    rval = c_long()
    ok = libsmjs.JS_EvaluateScript(cx, global_object, script, len(script), 'script', 1, byref(rval))

    p = libsmjs.JS_ValueToString(cx, rval)
    libsmjs.JS_GetStringBytes.restype = c_char_p
    return libsmjs.JS_GetStringBytes(p)
